﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Configuration.Provider;

namespace rpxnow_library.membership
{
    public class RoleProvider : System.Web.Security.RoleProvider
    {
        private string connectionString;
        private string applicationName;

        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (string.IsNullOrEmpty(name))
            {
                name = "IcklePicklesRolesProvider";
            }

            base.Initialize(name, config);

            string connectionStringName = Utils.GetInitializationConfigParameter(config, "connectionStringName", "");
            this.connectionString = Utils.GetConnectionStringFromName(connectionStringName);

            string applicationName = Utils.GetInitializationConfigParameter(config, "applicationName", "");

            Utils.HandleExcessAttributes(config);
        }

        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            using (var ctx = this.LoadContext())
            {
                var lUserNames = new List<string>(usernames);
                var lRoleNames = new List<string>(roleNames);

                var users = from u in ctx.Users
                            where lUserNames.Contains(u.displayName)
                            select u;

                var roles = from r in ctx.Roles
                            where lRoleNames.Contains(r.role1)
                            select r;

                foreach (var user in users)
                {
                    foreach (var role in roles)
                    {
                        var userRole = new data.UserRole();
                        userRole.fUserID = user.ID;
                        userRole.fRoleID = role.ID;

                        ctx.UserRoles.InsertOnSubmit(userRole);
                    }
                }

                ctx.SubmitChanges();
            }
        }

        public override void CreateRole(string roleName)
        {
            if (string.IsNullOrEmpty(roleName))
                throw new ProviderException("Role name cannot be empty or null.");

            using (var ctx = this.LoadContext())
            {
                if (!ctx.Roles.Any(r => r.role1 == roleName))
                {
                    var role = new data.Role();
                    role.role1 = roleName;
                    ctx.Roles.InsertOnSubmit(role);

                    ctx.SubmitChanges();
                }
            }
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            if (string.IsNullOrEmpty(roleName))
                throw new ProviderException("Role name cannot be empty or null.");

            using (var ctx = this.LoadContext())
            {
                var role = ctx.Roles.SingleOrDefault(r => r.role1 == roleName);
                if (role != null)
                {
                    if (throwOnPopulatedRole && ctx.UserRoles.Any(ur => ur.fRoleID == role.ID))
                    {
                        throw new Exception("Role is populated");
                    }

                    ctx.Roles.DeleteOnSubmit(role);

                    ctx.SubmitChanges();

                    return true;
                }
            }

            return false;
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            if (string.IsNullOrEmpty(roleName))
                throw new ProviderException("Role name cannot be empty or null.");

            return this.GetUsersInRole(roleName).Where(u => u.StartsWith(usernameToMatch)).ToArray();
        }

        public override string[] GetAllRoles()
        {
            using (var ctx = this.LoadContext())
            {
                return ctx.Roles.Select(r => r.role1).ToArray();
            }
        }

        public override string[] GetRolesForUser(string username)
        {
            if (string.IsNullOrEmpty(username))
                throw new ProviderException("User name cannot be empty or null.");

            using (var ctx = this.LoadContext())
            {
                var user = ctx.Users.SingleOrDefault(u => u.displayName == username);
                if (user != null)
                {
                    var userRoles = from ur in ctx.UserRoles
                                    where ur.fUserID == user.ID
                                    select ur.fRoleID;

                    var roles = from r in ctx.Roles
                                where userRoles.Contains(r.ID)
                                select r.role1;

                    return roles.ToArray();
                }
            }

            return new string[] { };
        }

        public override string[] GetUsersInRole(string roleName)
        {
            if (string.IsNullOrEmpty(roleName))
                throw new ProviderException("Role name cannot be empty or null.");

            using (var ctx = this.LoadContext())
            {
                var role = ctx.Roles.SingleOrDefault(r => r.role1 == roleName);
                if (role == null)
                    throw new ProviderException("Role Name does not exist");

                var userRoles = from ur in ctx.UserRoles
                                where ur.fRoleID == role.ID
                                select ur.fUserID;

                var users = from u in ctx.Users
                            where userRoles.Contains(u.ID)
                            select u.displayName;

                return users.ToArray();
            }
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            if (string.IsNullOrEmpty(username))
                throw new ProviderException("User name cannot be empty or null.");
            if (string.IsNullOrEmpty(roleName))
                throw new ProviderException("Role name cannot be empty or null.");


            using (var ctx = this.LoadContext())
            {
                var user = ctx.Users.SingleOrDefault(u => u.displayName == username);
                if (user != null)
                {
                    var role = ctx.Roles.SingleOrDefault(r => r.role1 == roleName);
                    if (role == null)
                        throw new ProviderException("roleName does not exist");

                    return ctx.UserRoles.Any(ur => ur.fUserID == user.ID && ur.fRoleID == role.ID);
                }
            }

            return false;
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            using (var ctx = this.LoadContext())
            {
                var lUserNames = new List<string>(usernames);
                var lRoleNames = new List<string>(roleNames);

                var users = from u in ctx.Users
                            where lUserNames.Contains(u.displayName)
                            select u;

                var roles = from r in ctx.Roles
                            where lRoleNames.Contains(r.role1)
                            select r;

                foreach (var user in users)
                {
                    foreach (var role in roles)
                    {
                        var userRole = ctx.UserRoles.SingleOrDefault(ur => ur.fUserID == user.ID && ur.fRoleID == role.ID);
                        if (userRole != null)
                        {
                            ctx.UserRoles.DeleteOnSubmit(userRole);
                        }
                    }
                }

                ctx.SubmitChanges();
            }
        }

        public override bool RoleExists(string roleName)
        {
            if (string.IsNullOrEmpty(roleName))
                throw new ProviderException("Role name cannot be empty or null.");

            using (var ctx = this.LoadContext())
            {
                return ctx.Roles.Any(r => r.role1 == roleName);
            }
        }

        private rpxnow_library.membership.data.Context LoadContext()
        {
            return Utils.LoadDataContext(this.connectionString);
        }

        public override string ApplicationName
        {
            get
            {
                return this.applicationName;
            }
            set
            {
                this.applicationName = value;
            }
        }
    }
}
